package com.example.demo.spring.ioc.case02;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * 注解处理器
 * 首先根据给定的组件的包名，扫描这个包，
 * 找出其中所有的被@Component注解标注的类，将类型的信息保存下来
 * 然后提供一个getBean（）方法，允许根据bean的id获取bean
 *
 * @author: htc
 * @date: Created in 14:36 2019/1/10.
 */
public class BeanFactory {
    private HashMap<String, Object> beanPool;
    private HashMap<String, String> componets;

    public BeanFactory(String packageName) {
        beanPool = new HashMap<>();

        scanComponents(packageName);
    }

    private void scanComponents(String packageName) {
        componets = ComponentScanner.getComponentClassName(packageName);
    }

    public Object getBean(String id) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        if (beanPool.containsKey(id)) {
            return beanPool.get(id);
        }
        if (componets.containsKey(id)) {
            Object bean = Class.forName(componets.get(id)).newInstance();
            bean = assemblyMember(bean);
            beanPool.put(id, bean);
            return getBean(id);
        }
        throw new ClassNotFoundException();
    }

    private Object assemblyMember(Object obj) throws NoSuchMethodException, IllegalAccessException, InstantiationException, ClassNotFoundException, InvocationTargetException {
        Class cl = obj.getClass();
        for (Field f : cl.getDeclaredFields()) {
            Autowire at = f.getAnnotation(Autowire.class);
            if (at != null) {
                Method setMethod = cl.getMethod("set" + captureName(f.getName()), f.getType());
                setMethod.invoke(obj, getBean(at.id()));
            }
        }
        return obj;
    }

    /**
     * 方法名称首字母大写转换  dat  --> Dat
     * @param name
     * @return
     */
    private String captureName(String name) {
        char[] cs = name.toCharArray();
        //首字符大写
        cs[0] -= 32;
        return String.valueOf(cs);
    }
}
